#include <windows.h>
#include "freeimage.h"
#include <string>
#include <vector>

std::string OpenFile(char *filter = "All Files (*.*)\0*.*\0", HWND owner = NULL) 
{
	OPENFILENAME ofn;
	char fileName[MAX_PATH] = "";
	ZeroMemory(&ofn, sizeof(ofn));

	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = owner;
	ofn.lpstrFilter = filter;
	ofn.lpstrFile = fileName;
	ofn.nMaxFile = MAX_PATH;
	ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
	ofn.lpstrDefExt = "";

	std::string fileNameStr;
	if ( GetOpenFileName(&ofn) )
	{
		fileNameStr = fileName;
	}
	return fileNameStr;
}

#define MALLOC64(size, type)		((type*)_aligned_malloc((size) * sizeof(type), 64))
#define FREE64(ptr)					_aligned_free(ptr)

typedef unsigned int Pixel;
struct Image
{
	Pixel* buffer;
	int width;
	int height;
};

Image* LoadPngImage(const char* fileName)
{
	Image* img = new Image();
	FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
	fif = FreeImage_GetFileType( fileName, 0 );
	if (fif == FIF_UNKNOWN) fif = FreeImage_GetFIFFromFilename( fileName );
	FIBITMAP* tmp = FreeImage_Load( fif, fileName );
	if (tmp)
	{
		FIBITMAP* dib = FreeImage_ConvertTo32Bits( tmp );
		FreeImage_Unload( tmp );
		img->width = FreeImage_GetWidth( dib );
		img->height = FreeImage_GetHeight( dib );
		img->buffer = MALLOC64(img->width * img->height, Pixel);
		//memcpy(img->buffer, dib->data, img->width * img->height * sizeof(Pixel));
		for(int y = 0; y <img->width; y++) 
		{
			Pixel* line = (Pixel*)FreeImage_GetScanLine(dib, img->height - 1 - y);
			const int yIdx = y * img->width;
			for(int x=0; x<img->width; ++x)
			{
				img->buffer[yIdx+x] = (line[x]&0xFF00FF00) | ((line[x]&0xFF0000)>>16) | ((line[x]&0xFF)<<16);
			}
		}
		FreeImage_Unload( dib );
		return img;
	}
	delete img;
	return 0;
}

static std::vector<Image*> images[361];
static std::string sName = "img";
Image* CreateSpriteFrom(std::string file)
{
	// first we extract the name
	size_t p1 = file.rfind("\\");
	size_t p2 = file.rfind("_");
	size_t p3 = file.rfind("_", p2-1);
	if (p2 == std::string::npos) return 0;
	if (p3 == std::string::npos) return 0;
	if (p1 == std::string::npos) p1 = 0;
	sName = file.substr(p1+1, p3-p1-1);
	printf("Building Sprite: %s\n", sName.c_str());

	// load all images with the same rotation
	char buffer[256];
	int rotCount = 0;
	int longest = 0;
	int frameSizeX = 0;
	int frameSizeY = 0;
	std::string path = file.substr(0, p3);
	for (int rot=0; rot<=360; ++rot)
	{
		int badFile = 0;
		bool validRot = false;
		for (int frame=0;;++frame)
		{
			sprintf_s(buffer, "%s_%d_%04d.png", path.c_str(), rot, frame);
			Image* img = LoadPngImage(buffer);
			if (img)
			{
				printf("Loaded file: %s\n", buffer);
				images[rot].push_back(img);
				frameSizeX = max(frameSizeX, img->width);
				frameSizeY = max(frameSizeY, img->height);
				if (!validRot) 
				{
					++rotCount;
					validRot = true;
				}
			}
			else
			{
				++badFile;
				if (badFile > 3) break;
			}
		}
		longest = max(longest, (int)images[rot].size());
	}

	// Building Sprite
	printf("Building Sprite....\n");
	Image* finalImage = new Image();
	finalImage->width = longest * frameSizeX;
	finalImage->height = rotCount * frameSizeY;
	finalImage->buffer = MALLOC64(finalImage->width * finalImage->height, Pixel);
	memset(finalImage->buffer, 0, finalImage->width * finalImage->height * sizeof(Pixel));
	int r = 0;
	for (int i=0; i<rotCount; ++i)
	{
		// Get Next Rotation
		while(images[r].size() == 0){ ++r; }
		printf("Building Rotation: %d\n", r);
		
		// Build Image
		for (unsigned int j=0; j<images[r].size(); ++j)
		{
			Image* img = images[r][j];
			for (int y=0; y<img->height; ++y)
			for (int x=0; x<img->width; ++x)
			{
				int xx = j * frameSizeX + x;
				int yy = i * frameSizeY + y;
				finalImage->buffer[yy * finalImage->width + xx] = img->buffer[y * img->width + x];
			}
			FREE64(images[r][j]->buffer);
			delete images[r][j];
		}

		++r;
	}
	return finalImage;
}

int main(int argc, char *argv[])
{
	printf("Sprite Builder started...\n");

	std::string ss = OpenFile();
	if (ss != "")
	{
		Image* img = CreateSpriteFrom(ss);

		// Build Image in freeimage format
		FIBITMAP* dib = FreeImage_Allocate(img->width, img->height, 32);
		//memcpy(dib->data, img->buffer, img->width * img->height * sizeof(Pixel));
		RGBQUAD col;
		for(int y=0; y<img->height; ++y)
		{
			const Pixel* src = img->buffer + y * img->width;
			for(int x=0; x<img->width; ++x)
			{
				col.rgbRed		= (src[x] & 0x000000FF) >> 0;
				col.rgbGreen	= (src[x] & 0x0000FF00) >> 8;
				col.rgbBlue		= (src[x] & 0x00FF0000) >> 16;
				col.rgbReserved	= (src[x] & 0xFF000000) >> 24;
				FreeImage_SetPixelColor(dib, x, img->height - y - 1, &col);
			}
		}

		// get dir
		char dir[512];
		GetCurrentDirectory(512, dir);

		// Save the Image
		char buffer[256];
		sprintf_s(buffer, "%s\\%s.png", dir, sName.c_str());
		printf("Saving Image: %s\n", buffer);
		FreeImage_Save(FIF_PNG, dib, buffer);
		FreeImage_Unload( dib );

		// clean up
		FREE64(img->buffer);
		delete img;
	}
	else
	{
		printf("No file selected!\n");
	}
   
	system("PAUSE");
    return 0;
}
